#include "stdafx.h"
#include "ip_pack_queue.h"
#include <pthread.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include "checksum.h"
#include <unistd.h>
#include "validate_url.h"

void* thread_checkurl(void *);
void skip_ori_ip_pack(int sockfd, char* data);
void redirect_url(int sockfd, char* data, char* url, int type, int reason);
bool get_url_from_data(char* data, uint32_t* pip, char* url);
/*
 * 初始化检测URL
 */
void checkurl_init()
{
	pthread_t		tid;
	int				i;

	ip_pack_init();
	// 启动检测线程
	for(i = 0; i < CHECKURL_THREAD; i++)
	{
		pthread_create(&tid, NULL, thread_checkurl, NULL);
	}
}

/*
 * 创建检测URL线程
 */
void* thread_checkurl(void * p)
{
	int		sockfd;
	int		on			= 1;
	MYSQL	mysql;
	int		quts;

	// 创建SOCKET
	sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
	if(sockfd < 0)
	{
		printf("socket error: %s\n", (char *)strerror(errno));
		return;
	}
	// 设置IP自己填写
	if( -1 == setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) )
	{
		printf("set sockopt error: %s\n", (char *)strerror(errno));
		return;
	}
	// 初始化查询
	validate_init(&mysql, &quts);
	// 循环从队列中取出要检测的URL
	while(true)
	{
		char		data[2048]		= {0};
		char		url[256]		= {0};
		int			type			= -1;
		int			reason			= 0;
		uint32_t	ip				= 0;

		// 从队列中取数数据
		if( false == ip_pack_get(data) )
		{
			usleep(100); // 暂停一秒
			continue;
		}
		// 从数据中分离出URL
		if( false == get_url_from_data(data, &ip, url) )
		{
			skip_ori_ip_pack(sockfd, data);
			continue;
		}
		printf("find requset: %s\n", url);
		// 验证URL 
		if( validate_url(&mysql, quts, ip, url, &type, &reason) )
		{
			skip_ori_ip_pack(sockfd, data);
			continue;
		}
		else
		{
			redirect_url(sockfd, data, url, type, reason);
			continue;
		}
	}
	// 退出 
	close(sockfd);
	validate_clean(&mysql, quts);

	return NULL;
}
/*
 * 转换字符串
 */
inline char* strlwr(char* str)
{
	char*	orig	= str;

	for(; '\0' != *str; str++)
	{
		*str = tolower(*str);
	}

	return orig;
}
/*
 * 分离出HOST
 */
bool get_url_from_data(char* data, uint32_t* pip, char* url)
{
	char*			phost		= NULL;
	int				i			= 0;
	struct iphdr*	ip			= (struct iphdr*)data;
	struct tcphdr*	tcp			= (struct tcphdr*)(data + (ip->ihl << 2));
	char*			pdata		= (char *)(data + (ip->ihl << 2) + (tcp->doff << 2));

	*pip = ip->saddr;
	// 查找Host:标志
	phost = (char *)strcasestr(pdata, "\nHost:");
	if(NULL == phost)
	{
		return false;
	}
	phost += 6;
	// 移出空格
	while('\x20' == *phost)
	{
		phost++;
	}
	// 看不是为空HOST
	if(0 == *phost || '\r' == *phost || '\n' == *phost)
	{
		return false;
	}
	// 复制HOST
	for(i = 0; i < 255; i++)
	{
		switch(phost[i])
		{
		case '\x20':
		case '\r':
		case '\n':
		case ':':
		case 0:
			{
				strlwr(url);
				return true; // 已经成功分析出域名
			}
		default:
			{
				url[i] = phost[i];
			}
		}
	}
	// 主机未到结尾，防止误判
	return false;
}
/*
 * 放行请求
 */
void skip_ori_ip_pack(int sockfd, char* data)
{
	struct iphdr*		ip		= (struct iphdr*)data;
	struct tcphdr*		tcp		= (struct tcphdr*)(data + (ip->ihl << 2));
	int					nlen	= ntohs(ip->tot_len);
	struct sockaddr_in	addr	= {0};

	addr.sin_family = AF_INET;
	addr.sin_port = tcp->dest;
	addr.sin_addr.s_addr = ip->daddr;

	if(-1 == sendto(sockfd, data, nlen, 0, (struct sockaddr*)&addr, sizeof(addr)))
	{
		printf("[error]%u.%u.%u.%u:%d => %u.%u.%u.%u:%d %s\n"
				, IPQUAD(ip->saddr), ntohs(tcp->source)
				, IPQUAD(ip->daddr), ntohs(tcp->dest)
				, strerror(errno));	
	}
}
/*
 * 重定向到指定网址
 */
void redirect_url(int sockfd, char* data, char* url, int type, int reason)
{
	char				buff[1400]		= {0};
	int					len				= sizeof(buff);
	struct iphdr*		newip			= (struct iphdr*)buff;
	struct tcphdr*		newtcp			= (struct tcphdr*)(buff + sizeof(struct iphdr));
	char*				newdata			= (char *)(buff + sizeof(struct iphdr) + sizeof(struct tcphdr));
	struct iphdr*		oriip			= (struct iphdr*)data;
	struct tcphdr*		oritcp			= (struct tcphdr*)(data + (oriip->ihl << 2));
	int					head_len		= sizeof(struct iphdr) + sizeof(struct tcphdr);
	struct sockaddr_in	addr			= {0};

	addr.sin_family = AF_INET;
	addr.sin_port = oritcp->source;
	addr.sin_addr.s_addr = oriip->saddr;
	// 构建IP头
	newip->version = IPVERSION;		// 版本号IPV4
	newip->ihl = sizeof(struct iphdr) >> 2; // IP数据包的头部长度
	newip->tos = 0;
	//newip->tot_len = htons(len);
	newip->id = 0;
	newip->frag_off = oriip->frag_off;
	newip->ttl = MAXTTL;
	newip->protocol = IPPROTO_TCP;
	newip->check = 0; // 让系统自动处理
	newip->daddr = oriip->saddr;
	newip->saddr = oriip->daddr;
	// 构建TCP头部
	newtcp->doff = sizeof(struct tcphdr) >> 2; // TCP头长度
	newtcp->source = oritcp->dest;
	newtcp->dest = oritcp->source;
	newtcp->seq = oritcp->ack_seq;
	newtcp->ack_seq = htonl(ntohl(oritcp->seq) + ntohs(oriip->tot_len) - (oriip->ihl << 2) - (oritcp->doff << 2));
	// 构建数据区
	snprintf(newdata, len - head_len - 1
			, "HTTP/1.1 302 Found\r\nLocation: " REDIRECT_URL "\r\nContent-Length: 0\r\nContent-Type: text/html\r\n\r\n"
			, url, type, reason);
	// 第一次发头回应包
	newip->tot_len = htons(head_len);
	newtcp->ack = 1;
	
	set_tcp_checksum1(newip);
	if(-1 == sendto(sockfd, buff, head_len, 0, (struct sockaddr*)&addr, sizeof(addr)))
	{
		printf("[error]sendto redirect to %u.%u.%u.%u:%d[ack] %s\n", IPQUAD(oriip->saddr), ntohs(oritcp->source)
				, strerror(errno));
	}
	// 第二次发送重定向数据
	newip->id = 1 << 8;
	newip->tot_len = htons(len);
	newtcp->psh = 1;
	
	set_tcp_checksum1(newip);
	if(-1 == sendto(sockfd, buff, len, 0, (struct sockaddr*)&addr, sizeof(addr)))
	{	
		printf("[error]sendto redirect to %u.%u.%u.%u:%d[psh] %s\n", IPQUAD(oriip->saddr), ntohs(oritcp->source)
				, strerror(errno));
	}
}

